/*
 * MessageIO.cpp
 *
 *  Created on: 10/12/2012
 *      Author: Julian Ovalle
 */

#ifndef MESSAGEIO_CPP_
#define MESSAGEIO_CPP_

#include <cstdio>
#include <unistd.h>
#include <string.h>

#include <socketInterface.h>
#include <MessageIO.h>
#include <cliMsgDef.h>

void print_msg_t(struct msg_t msg) {

    printf("\n msg:opts: %X ", msg.options);
    printf("action: %X ", msg.type);
    printf("len: %d ", msg.len);
    printf("nodeId: %s ", msg.nodeId);
    printf("data: ");
    for (uint8_t i = 0; i < msg.len; i++) {
        printf("%X", msg.data[i]);
    }
    printf("\n");
}

void printMsg(Message msg) {

    printf("\n Msg:opts: %X ", msg.getOptions());
    printf("Type: %X ", msg.getType());
    printf("Len: %d ", msg.getLen());
    printf("nodeId: %s ", msg.getNodeId());
    printf("data: ");
    for (uint8_t i = 0; i < msg.getLen(); i++) {
        printf("%X", msg.getData()[i]);
    }
    printf("\n");
}

list<node_t> MessageIO::getNodeList() {
    return _node_list;
}

int MessageIO::obtainResponse() {

    struct msg_t msg;
    uint8_t r = 0;

    if (socketReceive(_s, &msg, sizeof(struct msg_t), NULL, 0,
            GET_RESPONSE_TIMEOUT_SEC, GET_RESPONSE_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
        perror("getResponse.recvfrom.Error recibiendo del socket");
#endif
        flushSocket(_s);
        return (-1);
    }

    if (msg.len == 0) {
#ifdef USR_MSG_DEBUG
        fprintf(stderr, "\nmsg_len=0 ");
#endif
        flushSocket(_s);
        return (-1);
    }

    uint8_t *response = new uint8_t[msg.len];
    for (r = 0; r < msg.len; r++) {
        response[r] = msg.data[r];
    }

#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
    printf("\nresponse: ");
    for (r = 0; r < msg.len; r++) {
        printf("%X ", response[r]);
    }
#endif
    _response->setType(msg.type);
    _response->setData(response);
    _response->setLen(msg.len);
    strcpy(_response->getNodeId(), msg.nodeId);

#ifdef USR_MSG_DEBUG
    printMsg(*_response);
#endif
    return 0;
}

int MessageIO::MessageOutput(Message* msg, list<node_t> destList) {

    if (msg == NULL) {
        return -1;
    }

    if (!(destList.empty())) {
        if (SendMulticast(*msg, destList) < 0) {
#ifdef USR_MSG_DEBUG
            fprintf(stderr,"MessageOutput.SendMulticast.Error al enviar multicast\n");
#endif
            return -1;
        }

        return 0;
    } else if (msg->getNodeId() != NULL) {
        if (Send(*msg) < 0) {
#ifdef USR_MSG_DEBUG
            fprintf(stderr,"MessageOutput.Send.Error al enviar unicast\n");
#endif
            return -1;
        }
    }
    return 0;
}

Message* MessageIO::getResponse() {
    return _response;
}

void MessageIO::setResponse(Message *response) {
    _response = response;
}

int MessageIO::getSocket() {
    return _s;
}

struct sockaddr_in MessageIO::getServerName() {
    return _serverName;
}

int MessageIO::Init() {

    int s = openSocket( &_serverName );

    if (s < 0) {
#ifdef USR_MSG_DEBUG
        perror("MsgIO.Init.openSocket.Error al iniciar socket");
#endif
        return -1;
    }

    _s = s;

    if (requestNodeList() < 0) {
        return (-1);
    }

    _response = new Message;

    return 0;

}

int MessageIO::Exit() {
    close(getSocket());
    getNodeList().clear();

    return 0;
}

int MessageIO::Send(Message message) {

    int r = -2;

    struct msg_t msg;

    message.toStruct(&msg);

    if ((strcmp(msg.nodeId, BROADCAST_ID) == 0)
            && (msg.options & RESPONSE_REQUIRED_BIT)) {
#ifdef USR_MSG_DEBUG
        fprintf(stderr, "Solo en unicast.");
#endif
        return (-1);
    }

#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
#endif

    r = socketSend(_s, &msg, sizeof(struct msg_t), NULL,
            0, 0, SOCKET_SEND_TIMEOUT_MSEC);
    if (r < 0) {
#ifdef USR_MSG_DEBUG
        perror("sendMessage.socketSend.Error enviando al socket");
        return (-1);
#endif
    }

#ifdef USR_MSG_DEBUG
    printf("sent --> %d\n", r);
#endif
    int ret = -2;
    if (socketReceive(_s, &ret, sizeof(int), NULL, 0,
            SEND_MSG_RETURN_TIMEOUT_SEC, SEND_MSG_RETURN_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
        perror("sendMessage.socketReceive.Error recibiendo del socket");
#endif
        flushSocket(_s);
        return (-1);
    }

#ifdef USR_MSG_DEBUG
    printf("ret--> %d\n", ret);
#endif

    if(ret!=0) {
        flushSocket(_s);
    }

    if (ret >= 0 && (msg.options & RESPONSE_REQUIRED_BIT)) { //obtain response if it was requested

        if (obtainResponse() < 0) {
            return -1;
        }
        return ret;
    }

    return ret;

}

int MessageIO::SendMulticast(Message message, list<node_t> destList) {

    int r = -2;
    struct msg_t msg;

    message.toStruct(&msg);

    msg.options |= MULTICAST_BIT;
    memset(&msg.nodeId, 0, 20);
    msg.nodeId[0] = destList.size();

    if (msg.options & RESPONSE_REQUIRED_BIT) {
#ifdef USR_MSG_DEBUG
        fprintf(stderr, "Solo en unicast.");
#endif
        return (-1);
    }

#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
#endif

    r = socketSend(_s, &msg, sizeof(struct msg_t), NULL,
            0, 0, SOCKET_SEND_TIMEOUT_MSEC);
    if (r < 0) {
#ifdef USR_MSG_DEBUG
        perror("sendMulticast.socketSend.Error enviando mensaje");
        return (-1);
#endif
    }

#ifdef USR_MSG_DEBUG
    printf(" sent--> %d\n", r);
#endif

    for (list<node_t>::iterator it = destList.begin(); it != destList.end();
            ++it) {

        r = socketSend(_s, it->id, 20, NULL, 0, 0,
                SOCKET_SEND_TIMEOUT_MSEC);
        if (r < 0) {
#ifdef USR_MSG_DEBUG
            perror("sendMulticast.socketSend.Error enviando id");
            return (-1);
#endif
        }

#ifdef USR_MSG_DEBUG
        printf(" sent--> %d ", r);
        printf("id--> %s\n", it->id);
#endif
    }

    int ret = -2;

    if (socketReceive(_s, &ret, sizeof(int), NULL, 0,
            SEND_MULTICAST_RETURN_TIMEOUT_SEC,
            SEND_MULTICAST_RETURN_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
        perror("sendMulticast.socketReceive.Error recibiendo del socket");
#endif
        return (-1);
    }

#ifdef USR_MSG_DEBUG
    printf("ret--> %d\n", ret);
#endif
    return ret;

}

int MessageIO::requestNodeList() {

    int r = -2, size = -1;
    struct msg_t msg;
    uint8_t i = 0;

    msg.options = 0;
    memset(&msg.nodeId, 0, 20);
    msg.len = 0;
    msg.type = NODE_LIST;
#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
#endif

    r = socketSend(_s, &msg, sizeof(struct msg_t), NULL,
            0, 0, SOCKET_SEND_TIMEOUT_MSEC);
    if (r < 0) {
#ifdef USR_MSG_DEBUG
        perror("requestNodeList.socketSend.Error enviando al socket");
        return (-1);
#endif
    }

#ifdef USR_MSG_DEBUG
    printf(" sent--> %d\n", r);
#endif

    if ((r = socketReceive(_s, &size, sizeof(int), NULL, 0,
            NODE_LIST_REQUEST_TIMEOUT_SEC, NODE_LIST_REQUEST_TIMEOUT_MSEC))
            < 0) {
        if (r == -1) {
#ifdef USR_MSG_DEBUG
        perror("requestNodeList.socketReceive.Error recibiendo del socket");
#endif
        return (-1);
        }
        size = -1;
    }

    if (size <= 0) {
        fprintf(stderr, "\nNo nodes received.\n");
        return 0;
    }
    printf(" %d nodes.\n", size);

    if (!(_node_list.empty())) {
#ifdef USR_MSG_DEBUG
        printf("\nrequestNodeList borrando la lista de nodos.");
#endif
        _node_list.clear();

    }

    node_t node;

    for (i = 0; i < size; i++) {

        if (socketReceive(_s, node.id, sizeof(node.id), NULL, 0, 0,
                SOCKET_RCV_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
            perror("requestNodeList.socketReceive.Error recibiendo id");
#endif
            return (-1);
        } else {
            _node_list.push_back(node);
        }
    }

    for (list<node_t>::iterator it = _node_list.begin(); it != _node_list.end();
            ++it) {
        printf("id: %s\n", it->id);
    }

    return (0);

}

int MessageIO::updateNodeList() {

    int r = -2, size = -1;
    struct msg_t msg;

    msg.options = 0;
    memset(&msg.nodeId, 0, 20);
    msg.len = 0;
    msg.type = NODE_LIST_UPDATE;
#ifdef USR_MSG_DEBUG
    print_msg_t(msg);
#endif

    r = socketSend(_s, &msg, sizeof(struct msg_t), NULL,
            0, 0, SOCKET_SEND_TIMEOUT_MSEC);
    if (r < 0) {
#ifdef USR_MSG_DEBUG
        perror("updateNodeList.socketSend.Error enviando al socket");
        return (-1);
#endif
    }

#ifdef USR_MSG_DEBUG
    printf(" sent--> %d\n", r);
#endif

    r = socketReceive(_s, &size, sizeof(int), NULL, 0,
            NODE_LIST_UPDATE_TIMEOUT_SEC, 0);
    if (r < 0) {
        if (r == -1) {
#ifdef USR_MSG_DEBUG
            perror("updateNodeList.socketReceive.Error recibiendo tamaño");
#endif
            return (-1);
        }
        size = 0;
    }

    printf(" %d nodes.\n", size);

    if (size <= 0) {
        fprintf(stderr, "\nNo nodes received.\n");
    }

    printf("\nupdate is deleting list.");
    _node_list.clear();

    node_t j;

    for (uint8_t i = 0; i < size; i++) {

        if (socketReceive(_s, j.id, sizeof(j.id), NULL, 0, 0,
                SOCKET_RCV_TIMEOUT_MSEC) < 0) {
#ifdef USR_MSG_DEBUG
            perror("updateNodeList.socketReceive.Error recibiendo id");
#endif
            return (-1);
        } else {
            _node_list.push_back(j);
        }
    }

    for (list<node_t>::iterator it = _node_list.begin(); it != _node_list.end();
            ++it) {
        printf("id: %s", it->id);
    }

    return (0);
}

#endif /* MESSAGEIO_CPP_ */
